﻿/*
 * File: MapEditWindow.cs
 * Developer : Brent Larsen (brentoboy@gmail.com)
 * Date: 2011-AUG-9
 * Copyright (c) 2011 Brent Larsen.
 * Distributed under the terms of the GPL v3
 *  
 * This file is part of GenX-RPG
 * http://code.google.com/p/genx-rpg/ 
 * 
 * GenX-RPG is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GenX-RPG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GenX-RPG.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Rpg.Editor
{
    public partial class MapEditWindow : Form
    {
        private Map _map;
        private Dictionary<string, Bitmap> _tileBitmaps = new Dictionary<string, Bitmap>();
        private Bitmap _canvas;
        private int _currentTileId;
        private bool _saved = false;
        private AdventureDatabase _db;

        public MapEditWindow()
        {
            InitializeComponent();

            var walkOptions = new Dictionary<int, string>()
            {
                { Tile.BLOCK_NONE, "(none)"},
                { Tile.BLOCK_NORTH, "N"},
                { Tile.BLOCK_EAST, "E"},
                { Tile.BLOCK_SOUTH, "S"},
                { Tile.BLOCK_WEST, "W"},
                { Tile.BLOCK_NORTH | Tile.BLOCK_EAST, "NE"},
                { Tile.BLOCK_NORTH | Tile.BLOCK_SOUTH, "NS"},
                { Tile.BLOCK_NORTH | Tile.BLOCK_WEST, "NW"},
                { Tile.BLOCK_EAST | Tile.BLOCK_SOUTH, "ES"},
                { Tile.BLOCK_EAST | Tile.BLOCK_WEST, "EW"},
                { Tile.BLOCK_SOUTH | Tile.BLOCK_WEST, "SW"},
                { Tile.BLOCK_NORTH | Tile.BLOCK_EAST | Tile.BLOCK_SOUTH, "NES"},
                { Tile.BLOCK_NORTH | Tile.BLOCK_EAST | Tile.BLOCK_WEST, "NEW"},
                { Tile.BLOCK_NORTH | Tile.BLOCK_SOUTH | Tile.BLOCK_WEST, "NSW"},
                { Tile.BLOCK_EAST | Tile.BLOCK_SOUTH | Tile.BLOCK_WEST, "ESW"},
                { Tile.BLOCK_ALL, "All"}
            };
            
            list_Walk.DisplayMember = "Value";
            list_Walk.ValueMember = "Key";
            list_Walk.DataSource = walkOptions.ToKvpList(); 

            foreach(var tile in panel1.Controls.OfType<OwnerDraw>())
            {
                tile.Paint += new PaintEventHandler(tile_Paint);
                tile.Click += new EventHandler(tile_Click);
                //this.toolTip1.SetToolTip(tile, Tile.Get(int.Parse(tile.Tag.ToString())).Name);
            }
        }

        public MapEditWindow(AdventureDatabase db, int? mapID)
            :this()
        {
            Contract.Requires(db != null);
            _db = db;

            if (mapID.HasValue)
            {
                _map = _db.LoadMap(mapID.Value, null);
            }
            else
            {
                _map = new Map(0, "New Map", 25, 25);
                for (int x = 0; x < _map.Width; x++)
                {
                    for (int y = 0; y < _map.Height; y++)
                    {
                        _map.Cells[x, y] = new MapCell(0, 0, x, y, 0, Tile.BLOCK_ALL, 0);
                    }
                }
            }

            this.Text = "Map Editor [ID: {0}]".f(_map.Id);

            txtName.Text = _map.Name;
            txtWidth.Text = _map.Width.ToString();
            txtHeight.Text = _map.Height.ToString();

            _resizeCanvas();
            //tileIcon1.Refresh();
        }

        private void _resizeCanvas()
        {
            Canvas.Width = _map.Width * Tile.SQUARE_SIZE;
            Canvas.Height = _map.Height * Tile.SQUARE_SIZE;
            Canvas.Top = 0;
            Canvas.Left = 0;
            using (var g = Canvas.CreateGraphics())
            {
                if (_canvas != null)
                    _canvas.Dispose();

                _canvas = new Bitmap(Canvas.Width, Canvas.Height, g);
            }
            _drawCanvas();
        }
        
        private void _drawCanvas()
        {
            using (var g = Graphics.FromImage(_canvas))
            {
                // draw scroll color on background to clear any existing noise
                g.Clear(Color.Black);

                // draw known cell tiles
                for (int x = 0; x < _map.Width; x++)
                {
                    for (int y = 0; y < _map.Height; y++)
                    {
                        var cell = _map.Cells[x, y];
                        if (cell == null)
                        {
                            cell = new MapCell(0, _map.Id, x, y, 1, Tile.BLOCK_NONE, 0);
                            _map.Cells[x, y] = cell;
                        }

                        _drawCell(g, cell);
                    }
                }

                foreach (var portal in _map.Portals)
                {
                    int x1 = portal.X1 * Tile.SQUARE_SIZE;
                    int x2 = ((portal.X2 + 1) * Tile.SQUARE_SIZE) - 1;
                    int y1 = portal.Y1 * Tile.SQUARE_SIZE;
                    int y2 = ((portal.Y2 + 1) * Tile.SQUARE_SIZE) - 1;
                    g.DrawRectangle(Pens.Purple, x1 + 1, y1 + 1, x2 - x1 - 2, y2 - y1 - 2);
                }
            }
            Canvas.Refresh();
        }

        private void _updateCanvas(int x, int y)
        {
            using (var g = Graphics.FromImage(_canvas))
            {
                _drawCell(g, _map.Cells[x, y]);
            }

            Canvas.Refresh();
        }

        private void _drawCell(Graphics g, MapCell cell)
        {
            int x1 = cell.X * Tile.SQUARE_SIZE;
            int x2 = ((cell.X + 1) * Tile.SQUARE_SIZE) - 1;
            int y1 = cell.Y * Tile.SQUARE_SIZE;
            int y2 = ((cell.Y + 1) * Tile.SQUARE_SIZE) - 1;

            var tile = _map.Tiles[cell.TileId];
            var tileBitmap = _tileBitmaps[tile.BmpName];
            g.DrawImage(tileBitmap, new Rectangle(x1, y1, Tile.SQUARE_SIZE, Tile.SQUARE_SIZE), new Rectangle(tile.BmpX, tile.BmpY, Tile.SQUARE_SIZE, Tile.SQUARE_SIZE), GraphicsUnit.Pixel);

            if (cell.Walk != tile.Walk)
            {
                Pen pen;

                if (cell.Walk == Tile.BLOCK_NONE)
                {
                    pen = Pens.YellowGreen;
                    g.DrawRectangle(pen, x1, y1, x2 - x1, y2 - y1);
                }
                else if (cell.Walk == Tile.BLOCK_ALL)
                {
                    pen = Pens.Red;
                    g.DrawRectangle(pen, x1, y1, x2 - x1, y2 - y1);
                }
                else
                { // draw each side independantly
                    // North Border
                    pen = ((cell.Walk & Tile.BLOCK_NORTH) == 0) ? Pens.YellowGreen : Pens.Red;
                    g.DrawLine(pen, x1, y1, x2, y1);

                    // East Border
                    pen = ((cell.Walk & Tile.BLOCK_EAST) == 0) ? Pens.YellowGreen : Pens.Red;
                    g.DrawLine(pen, x2, y1, x2, y2);

                    // South Border
                    pen = ((cell.Walk & Tile.BLOCK_SOUTH) == 0) ? Pens.YellowGreen : Pens.Red;
                    g.DrawLine(pen, x1, y2, x2, y2);

                    // West Border
                    pen = ((cell.Walk & Tile.BLOCK_WEST) == 0) ? Pens.YellowGreen : Pens.Red;
                    g.DrawLine(pen, x1, y1, x1, y2);
                }
            }
        }


        private void Canvas_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawImage(_canvas, new Point(0, 0));
        }

        private void tile_Paint(object sender, PaintEventArgs e)
        {
            var tile = _map.Tiles[(sender as OwnerDraw).Tag.AsInt() ?? 0];
            e.Graphics.DrawImage(_tileBitmaps[tile.BmpName], new Rectangle(0, 0, Tile.SQUARE_SIZE, Tile.SQUARE_SIZE), new Rectangle(tile.BmpX, tile.BmpY, Tile.SQUARE_SIZE, Tile.SQUARE_SIZE), GraphicsUnit.Pixel);
        }

        private void tile_Click(object sender, EventArgs e)
        {
            var tileID = int.Parse((sender as OwnerDraw).Tag.ToString());
            _currentTileId = tileID;
            selectedTileBorder.Top = (sender as Control).Top - 1;
            selectedTileBorder.Left = (sender as Control).Left - 1;
            list_Walk.SelectedValue = _map.Tiles[tileID].Walk;
        }

        private void Canvas_Click(object sender, EventArgs e)
        {
            var clickPoint = Canvas.PointToClient(Cursor.Position);
            var cellX = clickPoint.X / Tile.SQUARE_SIZE;
            var cellY = clickPoint.Y / Tile.SQUARE_SIZE;

            var cell = _map.Cells[cellX, cellY];
            cell.Walk = (int)list_Walk.SelectedValue;
            cell.TileId = _currentTileId;
            //cell.UpdateTileDetails();

            _updateCanvas(cellX, cellY);
        }

        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                var cellX = e.X / Tile.SQUARE_SIZE;
                var cellY = e.Y / Tile.SQUARE_SIZE;
                try
                {
                    var cell = _map.Cells[cellX, cellY];
                    if (cell.TileId != _currentTileId || cell.Walk != (int)list_Walk.SelectedValue)
                    {
                        cell.Walk = (int)list_Walk.SelectedValue;
                        cell.TileId = _currentTileId;
                        _updateCanvas(cellX, cellY);
                    }
                } 
                catch {}
            }
        }

        private void cmdSave_Click(object sender, EventArgs e)
        {
            try
            {
                _save();
                this.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void _save()
        {
            _map.Name = txtName.Text;
            System.Diagnostics.Debug.Assert(_map.Width == txtWidth.Text.AsInt());
            System.Diagnostics.Debug.Assert(_map.Height == txtHeight.Text.AsInt());

            _db.SaveMap(_map);

            var menuMaps = (this.Parent as AdventureWindow).menuMaps;
            var menuItem = menuMaps.DropDownItems.OfType<ToolStripMenuItem>().SingleOrDefault(x => x.Tag.AsInt() == _map.Id);
            if (menuItem == null)
            {
                menuItem = new ToolStripMenuItem();
                menuItem.Name = "menuMap_Edit{0}".f(_map.Id);
                menuItem.Text = _map.Name;
                menuItem.Tag = _map.Id;
                //menuItem.Click += Program.MainMenuWindow.menuMap_Edit_Click;
                menuMaps.DropDownItems.Add(menuItem);
            }
            else
            {
                menuItem.Text = _map.Name;
            }

            _saved = true;
        }

        private void MapEditWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!_saved)
            {
                var response = MessageBox.Show("Save?", "Wait!", MessageBoxButtons.YesNoCancel);
                switch (response)
                {
                    case System.Windows.Forms.DialogResult.Yes:
                        try
                        {
                            _save();
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                            e.Cancel = true;
                        }
                        break;

                    case System.Windows.Forms.DialogResult.No:
                        e.Cancel = false;
                        break;

                    case System.Windows.Forms.DialogResult.Cancel:
                        e.Cancel = true;
                        break;

                    default:
                        Contract.UnexpectedCase(response.ToString());
                        break;
                }
            }
        }

        private void cmdUpdateSize_Click(object sender, EventArgs e)
        {
            var oldHeight = _map.Height;
            var oldWidth = _map.Width;
            var oldCells = _map.Cells;

            var newHeight = int.Parse(txtHeight.Text);
            var newWidth = int.Parse(txtWidth.Text);
            var newCells = new MapCell[newWidth, newHeight];

            for (int x = 0 ; x < Math.Min(oldWidth, newWidth); x++)
            {
                for(int y = 0; y < Math.Min(oldHeight, newHeight); y++)
                {
                    newCells[x,y] = oldCells[x,y];
                }
            }

            for (int x = 0 ; x < newWidth; x++)
            {
                for(int y = 0; y < newHeight; y++)
                {
                    if (newCells[x,y] == null)
                    {
                        newCells[x,y] = new MapCell(0, _map.Id, x, y, 0, Tile.BLOCK_ALL, 0);
                    }
                }
            }

            _map.Cells = newCells;
            _map.Width = newWidth;
            _map.Height = newHeight;

            _resizeCanvas();
        }

        MapCell rtClickCell = null;
        private void Canvas_MouseClick(object sender, MouseEventArgs e)
        {
        }

        private void Canvas_MouseDown(object sender, MouseEventArgs e)
        {
            if ((e.Button | MouseButtons.Right) == MouseButtons.Right)
            {
                var x = e.X / Tile.SQUARE_SIZE;
                var y = e.Y / Tile.SQUARE_SIZE;
                if (x >= 0 && x < _map.Width && y >= 0 && y < _map.Height)
                    rtClickCell = _map.Cells[x, y];
            }
        }

        private void Canvas_MouseUp(object sender, MouseEventArgs e)
        {

        }

        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
        {
            if (rtClickCell == null)
            {
                e.Cancel = true;
            }
            else
            {
                menuContext_CellLocation.Text = "Cell ( {0}, {1} )".f(rtClickCell.X, rtClickCell.Y);
                menuContext_CellLocation.Tag = rtClickCell;

                var portal = _map.Portals.FirstOrDefault(p => p.X1 <= rtClickCell.X && p.X2 >= rtClickCell.X && p.Y1 <= rtClickCell.Y && p.Y2 >= rtClickCell.Y);

                if (portal == null)
                {
                    menuContext_CreatePortal.Visible = true;

                    menuContext_EditPortal.Visible = false;
                    menuContext_EditPortal.Tag = null;

                    menuContext_DeletePortal.Visible = false;
                    menuContext_DeletePortal.Tag = null;

                    menuContext_GotoPortalMap.Visible = false;
                    menuContext_GotoPortalMap.Tag = null;
                }
                else
                {
                    menuContext_CreatePortal.Visible = false;

                    menuContext_EditPortal.Visible = true;
                    menuContext_EditPortal.Tag = portal;

                    menuContext_DeletePortal.Visible = true;
                    menuContext_DeletePortal.Tag = portal;

                    menuContext_GotoPortalMap.Visible = true;
                    menuContext_GotoPortalMap.Text = "Goto Map: {0}".f(portal.To_MapId);
                    menuContext_GotoPortalMap.Tag = portal.To_MapId;
                }

                rtClickCell = null;
            }
        }

        private void menuContext_CreatePortal_Click(object sender, EventArgs e)
        {
            var selectedCell = menuContext_CellLocation.Tag as MapCell;
            var window = new PortalEditWindow(new Portal(0, _map.Id, selectedCell.X, selectedCell.Y), _db.GetMapNames());
            window.FormClosed += new FormClosedEventHandler(portalWindow_FormClosed);
            window.Show(this);
        }

        private void menuContext_EditPortal_Click(object sender, EventArgs e)
        {
            var menuItem = sender as ToolStripMenuItem;
            var portal = menuItem.Tag as Portal;
            var window = new PortalEditWindow(portal, _db.GetMapNames());
            window.FormClosed += new FormClosedEventHandler(portalWindow_FormClosed);
            window.Show(this);
        }

        private void menuContext_DeletePortal_Click(object sender, EventArgs e)
        {
            var menuItem = sender as ToolStripMenuItem;
            var portal = menuItem.Tag as Portal;
            if (portal != null && DialogResult.OK == MessageBox.Show("Are you sure?", "Confirm Delete", MessageBoxButtons.OKCancel))
            {
                try
                {
                    if (portal.Id != 0)
                        _db.DeletePortal(portal.Id);

                    _map.Portals.Remove(portal);
                    _drawCanvas();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

        private void menuContext_GotoPortalMap_Click(object sender, EventArgs e)
        {
            var menuItem = sender as ToolStripMenuItem;
            var mapId = menuItem.Tag.AsInt();
            if (mapId != 0)
            {
                var window = new MapEditWindow(_db, mapId);
                window.Show(this.Parent);
            }
        }

        void portalWindow_FormClosed(object sender, FormClosedEventArgs e)
        {
            var editWindow = sender as PortalEditWindow;
            var portal = editWindow.Portal;
            if (portal.To_MapId != 0 && !_map.Portals.Contains(portal))
            {
                _map.Portals.Add(portal);
            }
            _drawCanvas();
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (DialogResult.OK == MessageBox.Show(this, "Are you sure?", "Confirm Delete", MessageBoxButtons.OKCancel))
            {
                try
                {
                    var menuMaps = (this.Parent as AdventureWindow).menuMaps;

                    if (_map.Id != 0)
                        _db.DeleteMap(_map.Id);

                    var menuItem = menuMaps.DropDownItems.OfType<ToolStripMenuItem>().SingleOrDefault(x => x.Tag.AsInt() == _map.Id);
                    
                    if (menuItem != null)
                        menuMaps.DropDownItems.Remove(menuItem);

                    this.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }

            }
        }

        private void btnShiftLeft_Click(object sender, EventArgs e)
        {
            for (int y = 0; y < _map.Height; y++)
            {
                // save the info about the row that is about to get bumped
                var tile0 = _map.Cells[0, y].TileId;
                var walk0 = _map.Cells[0, y].Walk;

                for (int x = 1; x < _map.Width; x++)
                {
                    _map.Cells[x - 1, y].TileId = _map.Cells[x, y].TileId;
                    _map.Cells[x - 1, y].Walk = _map.Cells[x, y].Walk;
                    //_map.Cells[x - 1, y].UpdateTileDetails();
                }

                // populate the bottom row with the stuff on the top
                _map.Cells[_map.Width - 1, y].TileId = tile0;
                _map.Cells[_map.Width - 1, y].Walk = walk0;
                //_map.Cells[_map.Width - 1, y].UpdateTileDetails();
            }
            _drawCanvas();
        }

        private void btnShiftRight_Click(object sender, EventArgs e)
        {
            for (int y = 0; y < _map.Height; y++)
            {
                // save the info about the bottom row that is about to get bumped
                var tileN = _map.Cells[_map.Width - 1, y].TileId;
                var walkN = _map.Cells[_map.Width - 1, y].Walk;

                for (int x = _map.Width - 1; x >= 1; x--)
                {
                    _map.Cells[x, y].TileId = _map.Cells[x - 1, y].TileId;
                    _map.Cells[x, y].Walk = _map.Cells[x - 1, y].Walk;
                    //_map.Cells[x, y].UpdateTileDetails();
                }

                // populate the bottom row with the stuff on the top
                _map.Cells[0, y].TileId = tileN;
                _map.Cells[0, y].Walk = walkN;
                //_map.Cells[0, y].UpdateTileDetails();
            }
            _drawCanvas();
        }

        private void btnShiftUp_Click(object sender, EventArgs e)
        {
            for (int x = 0; x < _map.Width; x++)
            {
                // save the info about the row that is about to get bumped
                var tile0 = _map.Cells[x, 0].TileId;
                var walk0 = _map.Cells[x, 0].Walk;

                for (int y = 1; y < _map.Height; y++)
                {
                    _map.Cells[x, y - 1].TileId = _map.Cells[x, y].TileId;
                    _map.Cells[x, y - 1].Walk = _map.Cells[x, y].Walk;
                    //_map.Cells[x, y - 1].UpdateTileDetails();
                }

                // populate the bottom row with the stuff on the top
                _map.Cells[x, _map.Height - 1].TileId = tile0;
                _map.Cells[x, _map.Height - 1].Walk = walk0;
                //_map.Cells[x, _map.Height - 1].UpdateTileDetails();
            }
            _drawCanvas();
        }

        private void btnShiftDown_Click(object sender, EventArgs e)
        {
            for (int x = 0; x < _map.Width; x++)
            {
                // save the info about the bottom row that is about to get bumped
                var tileN = _map.Cells[x, _map.Height -1].TileId;
                var walkN = _map.Cells[x, _map.Height - 1].Walk;

                for (int y = _map.Height - 1; y >= 1; y--)
                {
                    _map.Cells[x, y].TileId = _map.Cells[x, y - 1].TileId;
                    _map.Cells[x, y].Walk = _map.Cells[x, y - 1].Walk;
                }

                // populate the bottom row with the stuff on the top
                _map.Cells[x, 0].TileId = tileN;
                _map.Cells[x, 0].Walk = walkN;
            }
            _drawCanvas();
        }
    }
}
